;>*******************************************************************************************************************************
;*																																*
;*	File:	Neuron.asm																											*
;*																																*
;*	This file contains the functions for generating neurons.																	*
;*																																*
;<*******************************************************************************************************************************

include	Neuron.inc

ChooseNextPoint		proto	pAttractedNeurons:DWORD

;>*******************************************************************************************************************************
;*																																*
;*	Section:	Data																											*
;*																																*
;*																																*
;<*******************************************************************************************************************************

.data

;>*******************************************************************************************************************************
;*																																*
;*	Variable:	pSplitRandom																									*
;*																																*
;*																																*
;<*******************************************************************************************************************************
align	4
pSplitRandom	DWORD	?

;>*******************************************************************************************************************************
;*																																*
;*	Variable:	pPointRandom																									*
;*																																*
;*																																*
;<*******************************************************************************************************************************
align	4
pPointRandom	DWORD	?

;>*******************************************************************************************************************************
;*																																*
;*	Variable:	Neurons																											*
;*																																*
;*	The array of <Neuron> objects used for generating the neuron points															*
;*																																*
;<*******************************************************************************************************************************
align	4
Neurons	Neuron	NUM_CUBE_NEURONS DUP (<>)

;>*******************************************************************************************************************************
;*																																*
;*	Variable:	DistanceStep																									*
;*																																*
;*	This is 4 copies of the distance moved on each step.																		*
;*																																*
;<*******************************************************************************************************************************
align	16
DistanceStep	VECTOR	<0.04,0.04,0.04,0.04>

;>*******************************************************************************************************************************
;*																																*
;*	Variable:	HalfRecipDistanceSquared																						*
;*																																*
;*	This is half of the reciprocal of the square of the distance moved on each step.											*
;*																																*
;<*******************************************************************************************************************************
align	4
HalfRecipDistanceSquared	Real4	312.5

;>*******************************************************************************************************************************
;*																																*
;*	Variable:	FloatReductionFactor																							*
;*																																*
;*	2^(-24), for making random floats between 0.0 and 1.0 instead of between 0 and 2^24											*
;*																																*
;<*******************************************************************************************************************************
align	4
FloatReductionFactor	DWORD	33800000h

;>*******************************************************************************************************************************
;*																																*
;*	Variable:	NegativeTwo																										*
;*																																*
;*	Negative two as a 4-byte floating point number																				*
;*																																*
;<*******************************************************************************************************************************
align	4
NegativeTwo	Real4	-2.0

;>*******************************************************************************************************************************
;*																																*
;*	Variable:	All2Pi																											*
;*																																*
;*	This is 4 copies of 2*pi.																									*
;*																																*
;<*******************************************************************************************************************************
align	16
All2Pi	VECTOR	<6.2831853,6.2831853,6.2831853,6.2831853>

;>*******************************************************************************************************************************
;*																																*
;*	Variable:	InterNeuronAttraction																							*
;*																																*
;*	This is 4 copies of the value indicating how much different neurons attract each other, times 16 (number of points skipped	*
;*	on each iteration).																											*
;*																																*
;<*******************************************************************************************************************************
align	16
InterNeuronAttraction	VECTOR	<32.0,32.0,32.0,32.0>

;>*******************************************************************************************************************************
;*																																*
;*	Variable:	nPointsGenerated																								*
;*																																*
;*	The current number of points that have been generated by <GenerationNeurons>.												*
;*																																*
;<*******************************************************************************************************************************
align	4
nPointsGenerated	DWORD	0


;>*******************************************************************************************************************************
;*																																*
;*	Section:	Code																											*
;*																																*
;*																																*
;<*******************************************************************************************************************************

.code

;>*******************************************************************************************************************************
;*																																*
;*	Function:	GenerateNeurons																									*
;*																																*
;*	This generates the points for the specified number of neurons.																*
;*																																*
;*	Parameters:																													*
;*	nNeurons		- The number of neurons to generate																			*
;*	pCentres		- Address of the centre points of the neurons																*
;*	SplitSeed		- Random seed for the splitting random number generator														*
;*	PointSeed		- Random seed for the point location random number generator												*
;*	pDestPoints		- Address at which to store the neuron points																*
;*	pDestWeights	- Address at which to store the neuron weights																*
;*	pNeuronGraph	- Address of neuron graph or NULL if none																	*
;*																																*
;*	Local Variables:																											*
;*	EndOfNeurons	- Address of the end of the used part of the neurons array													*
;*	nNeuronsLeft	- Number of neurons complete																				*
;*	nPointsTotal	- The total number of points generated (to be returned)														*
;*																																*
;*	Returns:																													*
;*	eax	- The number of points generated																						*
;*																																*
;<*******************************************************************************************************************************
GenerateNeurons	proc	nNeurons:DWORD,pCentres:DWORD,SplitSeed:DWORD,PointSeed:DWORD,pDestPoints:DWORD,pDestWeights:DWORD,pNeuronGraph:DWORD
 LOCAL	nPointsTotal:DWORD
 LOCAL	nNeuronsLeft:DWORD
 LOCAL	EndOfNeurons:DWORD
	push	ebx
	push	esi
	push	edi

;Before starting to generate the neurons, create the random number generators and set their seeds.
	cmp		pPointRandom,NULL
	jne		PointRandomCreated
	invoke	LocalAlloc,LMEM_FIXED,sizeof RANDOM
	mov		pPointRandom,eax
PointRandomCreated:
	cmp		pSplitRandom,NULL
	jne		SplitRandomCreated
	invoke	LocalAlloc,LMEM_FIXED,sizeof RANDOM
	mov		pSplitRandom,eax
SplitRandomCreated:
	invoke	SetRandomSeed,pPointRandom,PointSeed
	invoke	SetRandomSeed,pSplitRandom,SplitSeed

;First, allocate the memory used for generating the neurons.
	mov		ebx,nNeurons
	shl		ebx,2
	lea		ebx,Neurons[ebx+ebx*4]
	mov		edi,offset Neurons
	mov		EndOfNeurons,ebx
ASSUME EDI:PTR Neuron
InitializeNextNeuron:
	invoke	LocalAlloc,LMEM_FIXED,MAX_POINTS_PER_NEURON*sizeof NeuronPoint + 0Fh
	mov		[edi].pPoints,eax
	mov		[edi].nPoints,NUM_INITIAL_PATHS_PER_NEURON
	invoke	LocalAlloc,LMEM_FIXED,MAX_PATHS_PER_NEURON*4
	mov		[edi].pPathQueue,eax
	mov		[edi].pQueueHead,eax
	mov		[edi].pQueueTail,eax
	add		edi,sizeof Neuron
	cmp		edi,ebx
	jne		InitializeNextNeuron
	
;Second, generate the initial points of all neurons
;They are created randomly with the splitting random number generator
;because they must be the same for different configurations of the same neurons.
	mov		edi,offset Neurons
	xor		ebx,ebx
	movaps	xmm2,DistanceStep
	mov		esi,pCentres
NextInitialNeuron:
	push	edi
	push	ebx
	mov		edi,[edi].pPoints
	add		edi,0Fh
	and		edi,not 0Fh
ASSUME EDI:PTR NeuronPoint
	lea		ebx,[edi+(NUM_INITIAL_PATHS_PER_NEURON*sizeof NeuronPoint)]
NextInitialNeuronPoint:
	invoke	GetRandomFloat,pSplitRandom
	movss	xmm3,xmm0					;xmm3 = u0,--,--,--
	invoke	GetRandomFloat,pSplitRandom
	movss	xmm1,xmm0					;xmm1 = u1,--,--,--
	shufps	xmm1,xmm3,11000000b			;xmm1 = u1,u1,u0,--
	invoke	GetRandomFloat,pSplitRandom
	movss	xmm1,xmm0					;xmm1 = u2,u1,u0,--
	addps	xmm1,xmm1					;xmm1 = 2*u2,2*u1,2*u0,--
	mulps	xmm1,xmm2					;xmm1 = dx',dy',dz',--
	subps	xmm1,xmm2					;
	movaps	xmm3,xmm1					;xmm3 = dx',dy',dz',--
	movaps	xmm4,xmm1					;xmm4 = dx',dy',dz',--
	addps	xmm1,[esi]					;xmm1 = x,y,z,--
	movaps	VECTOR ptr [edi].x,xmm1
	mov		[edi].weight,WEIGHTS_FACTOR_DWORD
	mulps	xmm3,xmm3					;xmm3 = dx'^2,dy'^2,dz'^2,--
	movss	xmm5,xmm3					;xmm5 = dx'^2,--,--,--
	movhlps	xmm6,xmm3					;xmm6 = dz'^2,--,--,--
	shufps	xmm3,xmm3,01b				;xmm3 = dy'^2,dx'^2,dx'^2,dx'^2
	addss	xmm6,xmm5					;xmm6 = dx'^2+dz'^2,--,--,--
	addss	xmm3,xmm6					;xmm3 = dx'^2+dy'^2+dz'^2,dx'^2,dx'^2,dx'^2
	rsqrtss	xmm3,xmm3					;xmm3 = 1/|d'|,dx'^2,dx'^2,dx'^2
	shufps	xmm3,xmm3,00000000b			;xmm3 = 1/|d'|,1/|d'|,1/|d'|,1/|d'|
	mulps	xmm3,xmm4					;xmm3 = dx'/|d'|,dy'/|d'|,dz'/|d'|,0
	mulps	xmm3,xmm2					;xmm3 = dx,dy,dz,0
	movaps	VECTOR ptr [edi].deltaX,xmm3
	add		nPointsGenerated,1
	add		edi,sizeof NeuronPoint
	cmp		edi,ebx
	jne		NextInitialNeuronPoint
	pop		ebx
	pop		edi
ASSUME EDI:PTR Neuron
	mov		edx,[edi].pPathQueue
	mov		eax,[edi].pPoints
	add		eax,0Fh
	and		eax,not 0Fh
	lea		ecx,[edx+NUM_INITIAL_PATHS_PER_NEURON*4]
NextInitialNeuronQueue:
	mov		[edx],eax
	add		edx,4
	add		eax,sizeof NeuronPoint
	cmp		edx,ecx
	jne		NextInitialNeuronQueue
	mov		[edi].pQueueHead,edx
	add		esi,10h
	add		edi,sizeof Neuron
	add		ebx,1
	cmp		ebx,nNeurons
	jne		NextInitialNeuron
	
;Third, cycle through all neurons repeatedly, generating points, until all neurons complete
	mov		nNeuronsLeft,ebx
NextGenerateStep:
	mov		edi,offset Neurons
	mov		ebx,pNeuronGraph
NextStepNeuron:
	mov		esi,[edi].pQueueTail
	cmp		[edi].pQueueHead,esi
	je		CheckNextStepNeuron
	cmp		[edi].nPoints,MAX_POINTS_PER_NEURON
	je		CheckNextStepNeuron
	mov		ecx,esi
	mov		esi,[esi]					;esi = pParent
	add		ecx,4
	mov		edx,[edi].pPathQueue
	add		edx,MAX_PATHS_PER_NEURON*4
	cmp		ecx,edx
	cmove	ecx,[edi].pPathQueue
	mov		[edi].pQueueTail,ecx
ASSUME ESI:PTR NeuronPoint
	invoke	ChooseNextPoint,ebx
	add		nPointsGenerated,1
ASSUME EAX:PTR NeuronPoint
	cmp		[eax].weight,CUTTING_THRESHOLD
	jb		NoAddFirstToQueue
	mov		ecx,[edi].pQueueHead
	mov		[ecx],eax
	add		ecx,4
	mov		edx,[edi].pPathQueue
	add		edx,MAX_PATHS_PER_NEURON*4
	cmp		ecx,edx
	cmove	ecx,[edi].pPathQueue
	mov		[edi].pQueueHead,ecx
NoAddFirstToQueue:
	add		[edi].nPoints,1
	cmp		[edi].nPoints,MAX_POINTS_PER_NEURON
	je		DoneStepNeuron
	invoke	GetRandomNumber,pSplitRandom
	cmp		eax,SPLITTING_THRESHOLD
	jae		NoSplit
	invoke	ChooseNextPoint,ebx
	add		nPointsGenerated,1
	cmp		[eax].weight,CUTTING_THRESHOLD
	jb		NoAddSecondToQueue
	mov		ecx,[edi].pQueueHead
	mov		[ecx],eax
	add		ecx,4
	mov		edx,[edi].pPathQueue
	add		edx,MAX_PATHS_PER_NEURON*4
	cmp		ecx,edx
	cmove	ecx,[edi].pPathQueue
	mov		[edi].pQueueHead,ecx
NoAddSecondToQueue:
	add		[edi].nPoints,1
	cmp		[edi].nPoints,MAX_POINTS_PER_NEURON
	je		DoneStepNeuron
NoSplit:
	mov		eax,[edi].pQueueTail
	cmp		[edi].pQueueHead,eax
	jne		CheckNextStepNeuron
DoneStepNeuron:
	sub		nNeuronsLeft,1
	jz		DoneGenerateSteps

CheckNextStepNeuron:
	add		edi,sizeof Neuron
	add		ebx,4*3
	cmp		edi,EndOfNeurons
	jne		NextStepNeuron
	jmp		NextGenerateStep
DoneGenerateSteps:

	mov		edi,offset Neurons
	mov		esi,pDestPoints
	mov		ebx,pDestWeights
	mov		nPointsTotal,0
NextCopyNeuron:
	mov		eax,[edi].pPoints
	add		eax,0Fh
	and		eax,not 0Fh
	mov		ecx,[edi].nPoints
	add		nPointsTotal,ecx
ASSUME ESI:PTR VECTOR
NextCopyPoint:
	movdqa	xmm0,VECTOR ptr [eax].x
	mov		edx,[eax].weight
	movdqa	[esi],xmm0
	mov		[esi].a,3F800000h	;1.0f
	mov		[ebx],edx
	add		eax,sizeof NeuronPoint
	add		esi,10h
	add		ebx,4
	sub		ecx,1
	jnz		NextCopyPoint
	invoke	LocalFree,[edi].pPoints
	invoke	LocalFree,[edi].pPathQueue
	add		edi,sizeof Neuron
	cmp		edi,EndOfNeurons
	jne		NextCopyNeuron
	mov		eax,nPointsTotal	;Return the number of points generated

ASSUME EDI:NOTHING,ESI:NOTHING,EAX:NOTHING
	pop		edi
	pop		esi
	pop		ebx
	ret
GenerateNeurons	endp

;>*******************************************************************************************************************************
;*																																*
;*	Function:	ChooseNextPoint																									*
;*																																*
;*	This generates several random points around the parent <NeuronPoint> and picks the one with the minimum approximated		*
;*	electric potential energy, adding it to the <Neuron>.																		*
;*																																*
;*	Parameters:																													*
;*	edi					- Address of the <Neuron>																				*
;*	esi					- Address of the parent <NeuronPoint>																	*
;*	pAttractedNeurons	- Address of set of 1 to 3 attracted neuron indices, or NULL if none									*
;*																																*
;*	Local Variables:																											*
;*	bestSumDistance	-																											*
;*																																*
;*	Returns:																													*
;*	eax	- Address of the new <NeuronPoint> in the <Neuron>'s point list															*
;*																																*
;<*******************************************************************************************************************************
ChooseNextPoint	proc	pAttractedNeurons:DWORD
 LOCAL	bestSumDistance:DWORD
ASSUME EDI:PTR Neuron,ESI:PTR NeuronPoint
	push	ebx
	push	edi

;This function implicitly calculates 2 angles for each of 8 points around a sphere of radius DistanceStep.
;The first angle, a, isDoneGenerating chosen as acos(1-2u), so cos(a) = 1-2u and sin(a) = 2sqrt(u(1-u))
;The second angle, b, isDoneGenerating chosen as 2pi*v
;This way, the points end up uniformly spread over the surface of the sphere.
;By this: dx = d*cos(a); dy = d*sin(a)cos(b); dz = d*sin(a)sin(b).

	mov		ebx,4					;generate 2 tuples of 4 random floats each
	jmp		StartGeneratingRandom
GenerateNextRandom:
	shufps	xmm2,xmm2,10010000b		;shift elements up one to fit the next ones in
	shufps	xmm3,xmm3,10010000b		;
	shufps	xmm6,xmm6,10010000b		;
	shufps	xmm7,xmm7,10010000b		;
StartGeneratingRandom:
	invoke	GetRandomFloat,pPointRandom
	movss	xmm2,xmm0				;xmm2 = u
	mulss	xmm0,NegativeTwo		;xmm0 = -2u
	movss	xmm3,xmm0				;xmm3 = -2u
	invoke	GetRandomFloat,pPointRandom
	movss	xmm6,xmm0				;xmm6 = u
	mulss	xmm0,NegativeTwo		;xmm0 = -2u
	sub		ebx,1
	movss	xmm7,xmm0				;xmm7 = -2u
	jnz		GenerateNextRandom

	movaps	xmm1,AllOne				;xmm1 = 1,1,1,1
	movaps	xmm5,xmm1				;xmm5 = 1,1,1,1
	addps	xmm3,xmm1				;xmm3 = 1-2u,...,...,... = cos(a),...,...,...
	addps	xmm7,xmm5				;xmm7 = 1-2u,...,...,... = cos(a),...,...,...
	subps	xmm1,xmm2				;xmm1 = 1-u,...,...,...
	subps	xmm5,xmm6				;xmm5 = 1-u,...,...,...
	mulps	xmm1,xmm2				;xmm1 = u(1-u),...,...,...
	mulps	xmm5,xmm6				;xmm5 = u(1-u),...,...,...
	rsqrtps	xmm1,xmm1				;xmm1 = 1/sqrt(u(1-u)),...,...,...
	rsqrtps	xmm5,xmm5				;xmm5 = 1/sqrt(u(1-u)),...,...,...
	rcpps	xmm1,xmm1				;xmm1 = sqrt(u(1-u)),...,...,...
	rcpps	xmm5,xmm5				;xmm5 = sqrt(u(1-u)),...,...,...
	addps	xmm1,xmm1				;xmm1 = 2sqrt(u(1-u)),...,...,... = sin(a),...,...,...
	addps	xmm5,xmm5				;xmm5 = 2sqrt(u(1-u)),...,...,... = sin(a),...,...,...
	movaps	xmm2,DistanceStep		;multiply them by the distance,
	mulps	xmm3,xmm2				;since dx, dy, and dz all have either
	mulps	xmm7,xmm2				;sin(a) or cos(a) multiplied by the distance
	mulps	xmm1,xmm2				;
	mulps	xmm5,xmm2				;

;Right now, the following has been calculated
;xmm3 = d*cos(a) 0-3
;xmm7 = d*cos(a) 4-7
;xmm1 = d*sin(a) 0-3
;xmm5 = d*sin(a) 4-7

	mov		ebx,4					;generate 2 tuples of 4 random floats each
	jmp		StartGeneratingRandomAngles
GenerateNextRandomAngle:
	shufps	xmm2,xmm2,10010000b		;shift elements up one to fit the next ones in
	shufps	xmm4,xmm4,10010000b		;
StartGeneratingRandomAngles:
	invoke	GetRandomFloat,pPointRandom
	movss	xmm2,xmm0				;xmm2 = u
	invoke	GetRandomFloat,pPointRandom
	sub		ebx,1
	movss	xmm4,xmm0				;xmm4 = u
	jnz		GenerateNextRandomAngle

	movaps	xmm6,All2Pi
	mulps	xmm2,xmm6					;xmm2 = 2pi*u,...,...,... = b,...,...,...
	mulps	xmm4,xmm6					;xmm4 = 2pi*u,...,...,... = b,...,...,...
	movaps	TempVectors[00h],xmm2
	movaps	TempVectors[10h],xmm4
	xor		ecx,ecx
DoTonsOfTrigonometry:
	fld		TempVectors[00h][ecx].x		;b0
	fld		TempVectors[00h][ecx].y		;b1		b0
	fld		TempVectors[00h][ecx].z		;b2		b1		b0
	fld		TempVectors[00h][ecx].a		;b3		b2		b1		b0
	fsincos								;cos b3	sin b3	b2		b1		b0
	fxch	st(4)							;b0		sin b3	b2		b1		cos b3
	fsincos								;cos b0	sin b0	sin b3	b2		b1		cos b3
	fxch	st(4)							;b1		sin b0	sin b3	b2		cos b0	cos b3
	fsincos								;cos b1	sin b1	sin b0	sin b3	b2		cos b0	cos b3
	fxch	st(4)							;b2		sin b1	sin b0	sin b3	cos b1	cos b0	cos b3
	fsincos								;cos b2	sin b2	sin b1	sin b0	sin b3	cos b1	cos b0	cos b3
	fxch	st(4)							;sin b3	sin b2	sin b1	sin b0	cos b2	cos b1	cos b0	cos b3
	fstp	TempVectors[20h][ecx].x		;sin b2	sin b1	sin b0	cos b2	cos b1	cos b0	cos b3
	fstp	TempVectors[20h][ecx].y		;sin b1	sin b0	cos b2	cos b1	cos b0	cos b3
	fstp	TempVectors[20h][ecx].z		;sin b0	cos b2	cos b1	cos b0	cos b3
	fstp	TempVectors[20h][ecx].a		;cos b2	cos b1	cos b0	cos b3
	fldz								;0		cos b2	cos b1	cos b0	cos b3
	fxch	st(4)							;cos b3	cos b2	cos b1	cos b0	0
	fstp	TempVectors[00h][ecx].x		;cos b2	cos b1	cos b0	0
	fstp	TempVectors[00h][ecx].y		;cos b1	cos b0	0
	fstp	TempVectors[00h][ecx].z		;cos b0	0
	fstp	TempVectors[00h][ecx].a		;0
	add		ecx,10h
	fstp	st(0)							;
	cmp		ecx,20h
	jne		DoTonsOfTrigonometry
	
;At this point, the cosine and sine of a pair of angles for each of 8 points have been calculated:
;xmm3				= d*cos(a) 0-3 = dx 0-3
;xmm7				= d*cos(a) 4-7 = dx 4-7
;xmm1				= d*sin(a) 0-3
;xmm5				= d*sin(a) 4-7
;TempVectors[00h]	= cos(b) 0-3
;TempVectors[10h]	= cos(b) 4-7
;TempVectors[20h]	= sin(b) 0-3
;TempVectors[30h]	= sin(b) 4-7

;Now calculate dy = d*sin(a)cos(b) and dz = d*sin(a)sin(b)
	movaps	xmm2,xmm1
	movaps	xmm6,xmm5
	mulps	xmm1,TempVectors[00h]	;xmm1 = dy 0-3
	mulps	xmm5,TempVectors[10h]	;xmm5 = dy 4-7
	mulps	xmm2,TempVectors[20h]	;xmm2 = dz 0-3
	mulps	xmm6,TempVectors[30h]	;xmm6 = dz 4-7
	movaps	TempVectors[00h],xmm3
	movaps	TempVectors[10h],xmm7
	movaps	TempVectors[20h],xmm1
	movaps	TempVectors[30h],xmm5
	movaps	TempVectors[40h],xmm2
	movaps	TempVectors[50h],xmm6

	mov		ebx,[edi].pPoints
	add		ebx,0Fh
	and		ebx,not 0Fh
	mov		ecx,[edi].nPoints
	shl		ecx,5					;NeuronPoint isDoneGenerating 32 bytes long (2^5)
	add		ebx,ecx					;ebx points to the new NeuronPoint
ASSUME EBX:PTR NeuronPoint
;Add up the 1/distance values to each point in the neuron
	xor		ecx,ecx
Next4SumDistances:
	movss	xmm0,[esi].x			;xmm0 = parent.x
	movss	xmm4,[esi].y			;xmm4 = parent.y
	movss	xmm7,[esi].z			;xmm7 = parent.z
	shufps	xmm0,xmm0,00000000b		;xmm0 = parent.x,parent.x,parent.x,parent.x
	shufps	xmm4,xmm4,00000000b		;xmm4 = parent.y,parent.y,parent.y,parent.y
	shufps	xmm7,xmm7,00000000b		;xmm7 = parent.z,parent.z,parent.z,parent.z
	addps	xmm0,xmm3				;xmm0 = x0,x1,x2,x3
	addps	xmm4,xmm1				;xmm4 = y0,y1,y2,y3
	addps	xmm7,xmm2				;xmm7 = z0,z1,z2,z3

	xorps	xmm5,xmm5				;xmm5 = 0,0,0,0 ; It will hold the sum of the 1/distance values
	mov		eax,[edi].pPoints
	add		eax,0Fh
	and		eax,not 0Fh
ASSUME EAX:PTR NeuronPoint
NextSumDistancesLocalNeuron:
	movaps	xmm1,VECTOR ptr [eax].x	;xmm1 = px,py,pz,--
	add		eax,sizeof NeuronPoint
	movlhps	xmm2,xmm1				;xmm2 = --,--,px,py
	movhlps	xmm3,xmm1				;xmm3 = pz,--,--,--
	shufps	xmm1,xmm1,00000000b		;xmm1 = px,px,px,px
	shufps	xmm2,xmm2,11111111b		;xmm2 = py,py,py,py
	cmp		eax,ebx					;The end of the NeuronPoints isDoneGenerating the location of the new NeuronPoint
	shufps	xmm3,xmm3,00000000b		;xmm3 = pz,pz,pz,pz
	subps	xmm1,xmm0				;xmm1 = px-x0,px-x1,px-x2,px-x3
	subps	xmm2,xmm4				;xmm2 = py-y0,py-y1,py-y2,py-y3
	subps	xmm3,xmm7				;xmm3 = pz-z0,pz-z1,pz-z2,pz-z3
	mulps	xmm1,xmm1				;xmm1 = (px-x0)^2,(px-x1)^2,(px-x2)^2,(px-x3)^2
	mulps	xmm2,xmm2				;xmm2 = (py-y0)^2,(py-y1)^2,(py-y2)^2,(py-y3)^2
	mulps	xmm3,xmm3				;xmm3 = (pz-z0)^2,(pz-z1)^2,(pz-z2)^2,(pz-z3)^2
	addps	xmm1,xmm2				;xmm1 = |pAttractedNeurons-p0|^2,|pAttractedNeurons-p1|^2,|pAttractedNeurons-p2|^2,|pAttractedNeurons-p3|^2
	addps	xmm1,xmm3				;
	rsqrtps	xmm1,xmm1				;xmm1 = 1/|pAttractedNeurons-p0|,1/|pAttractedNeurons-p1|,1/|pAttractedNeurons-p2|,1/|pAttractedNeurons-p3|
	addps	xmm5,xmm1				;xmm5+= 1/|pAttractedNeurons-p0|,1/|pAttractedNeurons-p1|,1/|pAttractedNeurons-p2|,1/|pAttractedNeurons-p3|
	jne		NextSumDistancesLocalNeuron

ASSUME EDI:NOTHING
	mov		edx,pAttractedNeurons
	test	edx,edx
	jz		SkipOtherNeurons
	push	edi
	xor		edi,edi
NextOtherNeuron:
	mov		edx,pAttractedNeurons
	mov		eax,[edx+edi]
	cmp		eax,-1
	je		CheckNextOtherNeuron
	shl		eax,2					;Neurons are 20 bytes each
	lea		eax,Neurons[eax+eax*4]	;
ASSUME EAX:PTR Neuron
	mov		edx,[eax].nPoints
	mov		eax,[eax].pPoints
	add		eax,0Fh
	and		eax,not 0Fh
ASSUME EAX:PTR NeuronPoint
	shl		edx,5					;NeuronPoints are 32 bytes each
	add		edx,eax
NextSumDistancesOtherNeuron:
	movaps	xmm1,VECTOR ptr [eax].x	;xmm1 = px,py,pz,--
	add		eax,sizeof NeuronPoint*16
	movlhps	xmm2,xmm1				;xmm2 = --,--,px,py
	movhlps	xmm3,xmm1				;xmm3 = pz,--,--,--
	shufps	xmm1,xmm1,00000000b		;xmm1 = px,px,px,px
	shufps	xmm2,xmm2,11111111b		;xmm2 = py,py,py,py
	cmp		eax,edx
	shufps	xmm3,xmm3,00000000b		;xmm3 = pz,pz,pz,pz
	subps	xmm1,xmm0				;xmm1 = px-x0,px-x1,px-x2,px-x3
	subps	xmm2,xmm4				;xmm2 = py-y0,py-y1,py-y2,py-y3
	subps	xmm3,xmm7				;xmm3 = pz-z0,pz-z1,pz-z2,pz-z3
	mulps	xmm1,xmm1				;xmm1 = (px-x0)^2,(px-x1)^2,(px-x2)^2,(px-x3)^2
	mulps	xmm2,xmm2				;xmm2 = (py-y0)^2,(py-y1)^2,(py-y2)^2,(py-y3)^2
	mulps	xmm3,xmm3				;xmm3 = (pz-z0)^2,(pz-z1)^2,(pz-z2)^2,(pz-z3)^2
	addps	xmm1,xmm2				;xmm1 = |pAttractedNeurons-p0|^2,|pAttractedNeurons-p1|^2,|pAttractedNeurons-p2|^2,|pAttractedNeurons-p3|^2
	addps	xmm1,xmm3				;
	rsqrtps	xmm1,xmm1				;xmm1 = 1/|pAttractedNeurons-p0|,1/|pAttractedNeurons-p1|,1/|pAttractedNeurons-p2|,1/|pAttractedNeurons-p3|
	mulps	xmm1,InterNeuronAttraction
	subps	xmm5,xmm1				;xmm5-= 1/|pAttractedNeurons-p0|,1/|pAttractedNeurons-p1|,1/|pAttractedNeurons-p2|,1/|pAttractedNeurons-p3|
	jb		NextSumDistancesOtherNeuron
CheckNextOtherNeuron:
	add		edi,4
	cmp		edi,4*3
	jne		NextOtherNeuron
	pop		edi
SkipOtherNeurons:

	test	ecx,ecx
	jnz		DoneSumDistances
	not		ecx						;This will make ecx non-zero for the second time around
	movaps	xmm6,xmm5
	movaps	xmm3,TempVectors[10h]
	movaps	xmm1,TempVectors[30h]
	movaps	xmm2,TempVectors[50h]
	jmp		Next4SumDistances
DoneSumDistances:
	movaps	TempVectors[60h],xmm6
	movaps	TempVectors[70h],xmm5

;Pick the point with the lowest sum, comparing as integers, with adjustment for negative numbers
	mov		eax,TempVectors[60h].x	;eax isDoneGenerating the value of the lowest sum so far
	xor		ecx,ecx					;ecx isDoneGenerating the offset of the lowest sum so far
	mov		edx,4					;edx isDoneGenerating the current offset
NextPickLowestSum:
	mov		edi,TempVectors[60h][edx].x
	test	edi,edi
	jns		NoAdjustForNegative
	xor		edi,7FFFFFFFh			;This reverses the order of the negatives to be like that of signed integers
NoAdjustForNegative:
	cmp		edi,eax
	cmovl	eax,edi
	cmovl	ecx,edx
	add		edx,4
	cmp		edx,4*NUM_ATTEMPT_POINTS
	jne		NextPickLowestSum
	movss	xmm0,TempVectors[00h][ecx].x	;xmm0 = best_dx,--,--,--
	movss	xmm1,TempVectors[20h][ecx].x	;xmm1 = best_dy,--,--,--
	movss	xmm2,TempVectors[40h][ecx].x	;xmm2 = best_dz,--,--,--
	movlhps	xmm0,xmm1						;xmm0 = best_dx,--,best_dy,--
	shufps	xmm0,xmm2,00001000b				;xmm0 = best_dx,best_dy,best_dz,best_dz
	movaps	xmm1,xmm0						;xmm1 = best_dx,best_dy,best_dz,best_dz
	addps	xmm0,VECTOR ptr [esi].x			;xmm0 = x,y,z,--
	movaps	VECTOR ptr [ebx].x,xmm0
	movaps	VECTOR ptr [ebx].deltaX,xmm1

;Lastly, calculate the new weight based on the dot product of the previous and new direction vectors
	mulps	xmm1,VECTOR ptr [esi].deltaX	;xmm1 = parent_dx*dx,parent_dy*dy,parent_dz*dz,--
	movhlps	xmm0,xmm1						;xmm0 = parent_dz*dz,--,--,--
	addss	xmm0,xmm1						;xmm0 = parent_dx*dx+parent_dz*dz,--,--,--
	shufps	xmm1,xmm1,01b					;xmm1 = parent_dy*dy,parent_dx*dx,parent_dx*dx,parent_dx*dx
	addss	xmm0,xmm1						;xmm0 = parent_dx*dx+parent_dy*dy+parent_dz*dz = parent_delta.delta
	mulss	xmm0,HalfRecipDistanceSquared	;xmm0 = parent_delta.delta/(distance^2)/2			; It's in the range -0.5 to +0.5
	addss	xmm0,AllHalf.x					;xmm0 = (1+parent_delta.delta/(distance^2))/2		; It's in the range 0 to 1
	rsqrtss	xmm0,xmm0						;xmm0 = ((1+parent_delta.delta/(distance^2))/2)^(1/8);It's still in the range 0 to 1
	rsqrtss	xmm0,xmm0						;
	rsqrtss	xmm0,xmm0						;
	rcpss	xmm0,xmm0						;
	mulss	xmm0,[esi].weight				;													; It's still in the range 0 to 1
	movss	[ebx].weight,xmm0
	mov		eax,ebx
	pop		edi
	pop		ebx
ASSUME ESI:NOTHING,EAX:NOTHING,EBX:NOTHING
	ret
ChooseNextPoint	endp


